package com.tju.resource.business.log.aspect;

import cn.hutool.json.JSONUtil;
import com.tju.resource.annotation.Log;
import com.tju.resource.common.ServiceException;
import com.tju.resource.mapper.LogOperationMapper;
import com.tju.resource.model.log.LogOperation;
import com.tju.resource.model.log.OperationLogTool;
import com.tju.resource.tool.tool.ChineseExceptionUtils;
import com.tju.resource.tool.tool.IpUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

/**
 * 日志切面
 *
 * @Author CLS
 * @Date 2022/4/25 16:46
 */
@Aspect
@Component
public class AopLog {

    @Autowired
    private ChineseExceptionUtils zh_ch;
    @Resource
    private LogOperationMapper operationMapper;

    /**
     * 注解定义切点，标记方法
     */

    @Pointcut("@annotation(com.tju.resource.annotation.Log)")
    public void operationLog() {
    }

    /**
     * 使用环绕通知
     *
     * @param joinPoint
     * @return
     * @throws Throwable
     */
//    @Around(value = "operationLog()")
    @AfterReturning(value = "operationLog()", returning = "returnValue")
    public void myLogger(JoinPoint joinPoint, Object returnValue) throws Throwable {
        //方法执行完成后增加日志
        addOperationLog(joinPoint, returnValue);
    }

    private void addOperationLog(JoinPoint joinPoint, Object returnValue) throws NoSuchMethodException {
        OperationLogTool<String> operationLogTool = new OperationLogTool<>();
        //使用ServletRequestAttributes请求上下文获取方法更多
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        assert attributes != null;
        HttpServletRequest request = attributes.getRequest();
        //TODO：系统模块
        String systemModule = request.getHeader("operation-module");
        if (systemModule.equals("undefined")) throw new ServiceException(zh_ch.MissingParameters);
        operationLogTool.setSystemModule(URLDecoder.decode(systemModule, StandardCharsets.UTF_8));

        //获取注解中的信息
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //拦截方法名称
        String name = joinPoint.getSignature().getName();
        Method method = joinPoint.getTarget().getClass().getMethod(name, signature.getParameterTypes());
        Log log = method.getAnnotation(Log.class);
        //TODO：操作类型
        operationLogTool.setOperationType(log.type().getType());
        //TODO：登录名
        String userName = request.getHeader("login-user-name");
        if (userName.equals("undefined")) throw new ServiceException(zh_ch.MissingParameters);
        operationLogTool.setUserName(userName);
        //TODO：类路径(方法)
        operationLogTool.setClasspath(joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()");
        //TODO：请求方法
        operationLogTool.setRequestMethod(request.getMethod());
        //TODO：IP
        String ip = IpUtil.getIpAddr(request);
        operationLogTool.setIp(ip);
        //TODO：地点
        String place = IpUtil.getIpRegion(ip);
        place = (place.contains("内网IP")) ? "内网IP" : place;
        operationLogTool.setPlace(place);
        //TODO：请求参数
        String requestParameter = Arrays.toString(joinPoint.getArgs());
        operationLogTool.setRequestParameter((requestParameter.length() > 1000) ? requestParameter.substring(0, 1000) : requestParameter);
        //TODO：请求地址
        operationLogTool.setRequestAddress(request.getRequestURI());
        //TODO：返回参数
        String returnParameter = JSONUtil.parse(returnValue).toString();
        returnParameter = returnParameter.length() > 1000 ? returnParameter.substring(0, 1000) : returnParameter;
        operationLogTool.setReturnParameter(returnParameter);

        operationMapper.insert(new LogOperation(JSONUtil.parseObj(operationLogTool).toString()));
    }
}
